package top.soft.util;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.net.URISyntaxException;
import java.net.UnknownHostException;
import java.util.Map;
import java.util.Map.Entry;

import javax.net.ssl.SSLException;

import org.apache.commons.codec.Charsets;
import org.apache.http.HttpEntity;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;

import com.fasterxml.jackson.databind.ObjectMapper;

import top.soft.bean.User;

/**
 * @ClassName: NetWork
 * @Author: Amosen
 * @Since: 2021/4/19
 */

// 发起请求的工具类
public class NetWork {

	private static HttpClient httpClient;

	private static final int MAX_CONNECTION = 100;

	private static final int MAX_CONCURRENT_CONNECTIONS = 100;

	// 建立连接的超时时间
	private static final int CONNECTION_TIME_OUT = 30 * 1000;

	// 请求超时时间
	private static final int REQUEST_TIME_OUT = 30 * 1000;

	// 重试次数
	private static final int MAX_FAIL_RETRY_COUNT = 3;

	// 请求配置
	private static RequestConfig requestConfig;

	static {
		SocketConfig socketConfig = SocketConfig.custom().setSoTimeout(REQUEST_TIME_OUT).setSoKeepAlive(true)
				.setTcpNoDelay(true).build();
		requestConfig = RequestConfig.custom().setSocketTimeout(REQUEST_TIME_OUT).setConnectTimeout(CONNECTION_TIME_OUT)
				.build();
		PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager();
		connManager.setMaxTotal(MAX_CONNECTION);
		connManager.setDefaultMaxPerRoute(MAX_CONCURRENT_CONNECTIONS);
		connManager.setDefaultSocketConfig(socketConfig);

		// 重试处理器
		httpClient = HttpClients.custom().setConnectionManager(connManager)
				.setRetryHandler(new MyHttpRequestRetryHandler()).build();
	}

	// FIXME: 这里需要提供发起网络请求的方法，包括doGet和doPost方法，并将结果封装为Response对象返回
	// FIXME: Response对象需要的数据：status_code: 响应状态码，是服务器返回的状态码，不是HTTP响应码
	// FIXME: 当HTTP请求不成功时返回null（即当http响应码为200时，返回null）

	public static Response service(Request request) {
		if (request == null) {
			return null;
		} else {
			if (request.getMethod().equals(Request.GET)) {
				return doGet(request);
			} else if (request.getMethod().equals(Request.POST)) {
				return doPost(request);
			}
			return null;
		}
	}

	// 发送get请求
	public static Response doGet(Request request) {
		if (request == null) {
			return null;
		}
		try {
			URIBuilder uriBuilder = new URIBuilder(request.getUrl());
			Map<String, String> params = request.getData();
			// 添加请求参数
			if (params != null) {
				for (Map.Entry<String, String> entry : params.entrySet()) {
					uriBuilder.addParameter(entry.getKey(), entry.getValue());
				}
			}
			HttpGet httpGet = new HttpGet(uriBuilder.build());

			httpGet.setConfig(requestConfig);
			HttpResponse httpResponse = httpClient.execute(httpGet);

			if (httpResponse.getStatusLine().getStatusCode() != 200) {
				return null;
			} else {
				String responseStr = EntityUtils.toString(httpResponse.getEntity(), Charsets.UTF_8);
				ObjectMapper om = new ObjectMapper();
				Response response = om.readValue(responseStr, Response.class);
				return response;
			}
		} catch (URISyntaxException | IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	// 发送POST请求
	public static Response doPost(Request request) {
		try {
			if (request == null) {
				return null;
			}
			URIBuilder uriBuilder = new URIBuilder(request.getUrl());
			Map<String, String> params = request.getData();
			if (params != null) {
				for (Map.Entry<String, String> entry : params.entrySet()) {
					uriBuilder.addParameter(entry.getKey(), entry.getValue());
				}
			}

			HttpPost httpPost = new HttpPost(uriBuilder.build());

			httpPost.setConfig(requestConfig);

			HttpResponse httpResponse = httpClient.execute(httpPost);
			if (httpResponse.getStatusLine().getStatusCode() != 200) {
				return null;
			} else {
				String responseStr = EntityUtils.toString(httpResponse.getEntity(), Charsets.UTF_8);
				ObjectMapper om = new ObjectMapper();
				Response response = om.readValue(responseStr, Response.class);
				return response;
			}
		} catch (URISyntaxException | IOException e) {
			e.printStackTrace();
		}

		return null;
	}

	private static class MyHttpRequestRetryHandler implements HttpRequestRetryHandler {

		@Override
		public boolean retryRequest(IOException e, int executionTimes, HttpContext httpContext) {
			if (executionTimes >= MAX_FAIL_RETRY_COUNT) {
				return false;
			}
			if (e instanceof InterruptedIOException) {
				// 超时
				return false;
			}
			if (e instanceof UnknownHostException) {
				// 未知主机
				return false;
			}
			if (e instanceof ConnectTimeoutException) {
				// 连接被拒绝
				return false;
			}
			if (e instanceof SSLException) {
				// SSL handshake exception
				return false;
			}

			HttpClientContext clientContext = HttpClientContext.adapt(httpContext);
			HttpRequest request = clientContext.getRequest();
			boolean idempotent = !(request instanceof HttpEntityEnclosingRequest);
			if (idempotent) {
				return true;
			}
			return false;
		}
	}

	public static Response uploadFile(Request request, String fileName) {
		CloseableHttpClient httpClient = HttpClients.createDefault();
		try {
			URIBuilder uriBuilder = new URIBuilder(request.getUrl());
			Map<String, String> params = request.getData();
			if (params != null) {
				for (Entry<String, String> entry : params.entrySet()) {
					uriBuilder.addParameter(entry.getKey(), entry.getValue());
				}
			}

			HttpPost httpPost = new HttpPost(uriBuilder.build());
			httpPost.setConfig(requestConfig);
			File uploadFile = new File(fileName);
			if (!uploadFile.exists()) {
				return null;
			}
			FileBody bin = new FileBody(uploadFile);
			StringBody comment = new StringBody(fileName, ContentType.TEXT_PLAIN);
			HttpEntity reqEntity = MultipartEntityBuilder.create().addPart("file", bin).addPart("filename", comment)
					.build();
			httpPost.setEntity(reqEntity);
			httpPost.addHeader("enctype", "multipart/form-data");
			HttpResponse httpResponse = httpClient.execute(httpPost);
			if (httpResponse.getStatusLine().getStatusCode() != 200) {
				return null;
			} else {
				String responseEntity = EntityUtils.toString(httpResponse.getEntity(), Charsets.UTF_8);
				ObjectMapper om = new ObjectMapper();
				Response response = om.readValue(responseEntity, Response.class);

				return response;
			}
		} catch (Exception e) {
			// TODO: handle exception
			return null;
		}
	}

	public static Response downloadFile(String url, String localPath, Integer fileId) throws IOException {
		if (localPath == null || url == null) {
			return null;
		}
		if (localPath.length() == 0 || localPath.trim().length() == 0) {
			return null;
		}
		if (url.length() == 0 || url.trim().length() == 0) {
			return null;
		}
		if (CommonData.getUser() == null) {
			return null;
		}

		User loginUser = CommonData.getUser();
		String loginAccount = loginUser.getAccount();

		URIBuilder uriBuilder;

		try {
			uriBuilder = new URIBuilder(url);
			uriBuilder.addParameter("loginAccount", loginAccount);
			uriBuilder.addParameter("fileId", String.valueOf(fileId));
		} catch (URISyntaxException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return null;
		}

		HttpClient client = HttpClients.createDefault();
		HttpGet httpGet;
		try {
			httpGet = new HttpGet(uriBuilder.build());
		} catch (URISyntaxException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return null;
		}
		HttpResponse response = client.execute(httpGet);

		HttpEntity entity = response.getEntity();

		try {
			if (response.getStatusLine().getStatusCode() != 200) {
				return null;
			} else {
				InputStream in = entity.getContent();
				File file = new File(localPath);
				FileOutputStream out = new FileOutputStream(file);
				byte[] buffer = new byte[1024];
				int ch = 0;
				while ((ch = in.read(buffer)) != -1) {
					out.write(buffer, 0, ch);
				}
				in.close();
				out.flush();
				out.close();
				Response returnResponse = new Response();
				returnResponse.setCode(200);
				return returnResponse;
			}
		} catch (Exception castException) {
			// TODO: handle exception
			try {
				String responseStr = EntityUtils.toString(entity, Charsets.UTF_8);
				ObjectMapper om = new ObjectMapper();
				Response localResponse = om.readValue(responseStr, Response.class);
				return localResponse;
			} catch (Exception transformException) {
				// TODO: handle exception
				return null;
			}
		}
	}

}
